﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data;

namespace Shop_Inventory_Management.Controller
{
    class SynchronManager
    {
        private static SynchronManager SynchronManagerInstance;
        private static Database.DBManager DBController;
        /**************************** PrimaryKey ************************************/
        private static List<string> ManufacturerPrimaryKey = new List<string>(Constant.MANUFACTURER_PRIMARYKEY_NUMBER);
        private static List<string> ProductPrimaryKey = new List<string>(Constant.PRODUCT_PRIMARYKEY_NUMBER);
        private static List<string> StockPrimaryKey = new List<string>(Constant.STOCK_PRIMARYKEY_NUMBER);
        private static List<string> DiscountPrimaryKey = new List<string>(Constant.DISCOUNT_PRIMARYKEY_NUMBER);
        private static List<string> ReportPrimaryKey = new List<string>(Constant.REPORT_PRIMARYKEY_NUMBER);

        public static SynchronManager getInstance()
        {
            if (SynchronManagerInstance == null)
            {
                SynchronManagerInstance = new SynchronManager();
            }
            return SynchronManagerInstance;
        }

        private SynchronManager()
        {
            try
            {
                DBController = Database.DBManager.getInstance();
                PrimaryKeyInitialization();
            }
            catch
            {
                throw;
            }
        }

        private static void PrimaryKeyInitialization()
        {
            ManufacturerPrimaryKey.Add(Constant.MANUFACTURER_NAME);
            ProductPrimaryKey.Add(Constant.PRODUCT_ID);
            StockPrimaryKey.Add(Constant.STOCK_PRODUCT_ID);
            StockPrimaryKey.Add(Constant.STOCK_BATCH_ID);
            DiscountPrimaryKey.Add(Constant.DISCOUNT_PRODUCT_ID);
            ReportPrimaryKey.Add(Constant.REPORT_SHOP_ID);
            ReportPrimaryKey.Add(Constant.REPORT_DATE);
        }

        #region Synchronization

        public void SynchronizeTables()
        {
            try
            {
                DateTime synTime = DateTime.Now;
                DBController.RefleshDataSet();
                SynLog.Log(synTime.ToString());
                SynLog.Log(Constant.LOG_START_DAILY_SYN);
                SynchronizeManufacturer();
                SynchronizeProduct();
                SynchronizeStock();
                SynchronizeDiscount();
           //     SynchronizeReport();
                TimeSpan diff = DateTime.Now - synTime;
                SynLog.Log(Constant.LOG_END_DAILY_SYN);
            }
            catch
            {
                throw;
            }
        }

        private Dictionary<string, DataRow> StoreTableIntoDictionary(string tableName, List<string> keys, string server)
        {
            DataSet tableList = new DataSet();
            switch (server)
            {
                case Constant.HQ_SERVER:
                    tableList = DBController.GetHQDsTableList();
                    break;

                case Constant.SHOP_SERVER:
                    tableList = DBController.GetShopDsTableList();
                    break;
            }
            Dictionary<string, DataRow> dictionary = new Dictionary<string, DataRow>();
            foreach (DataRow dr in tableList.Tables[tableName].Rows)
            {
                string key = FindKey(keys, dr);
                dictionary.Add(key, dr);
            }
            return dictionary;
        }

        private string FindKey(List<string> keys, DataRow dr)
        {
            string primaryKey = string.Empty;
            foreach (string key in keys)
                primaryKey += dr[key].ToString();
            return primaryKey;
        }

        #endregion

        #region Manufacturer Operation

        private void SynchronizeManufacturer()
        {
            SynLog.Log(Constant.LOG_START_MANUFACTURER);
            DataTable shopTable = DBController.GetShopDsTableList().Tables[Constant.MANUFACTURER_TABLE];
            DataTable hqTable = DBController.GetHQDsTableList().Tables[Constant.MANUFACTURER_TABLE];
            try
            {
                Dictionary<string, DataRow> HQManufacturerDic = StoreTableIntoDictionary(Constant.MANUFACTURER_TABLE, ManufacturerPrimaryKey, Constant.HQ_SERVER);
                Dictionary<string, DataRow> ShopManufacturerDic = StoreTableIntoDictionary(Constant.MANUFACTURER_TABLE, ManufacturerPrimaryKey, Constant.SHOP_SERVER);
                SynchronizeManufacturerRows(shopTable, hqTable, HQManufacturerDic, ShopManufacturerDic);
                DBController.UpdateDatabase(Constant.MANUFACTURER_TABLE);
            }
            catch
            {
                throw;
            }
            SynLog.Log(Constant.LOG_END_MANUFACTURER);
        }

        private void SynchronizeManufacturerRows(DataTable shopTable, DataTable hqTable, Dictionary<string, DataRow> ManufacturerDic, Dictionary<string, DataRow> ShopManufacturerDic)
        {
            foreach (DataRow dr in shopTable.Rows)
            {
                DataRow newRow;
                string PrimaryKey = FindKey(ManufacturerPrimaryKey, dr);
                bool isFound = ManufacturerDic.TryGetValue(PrimaryKey, out newRow);
                if (isFound)
                {
                    UpdateManufacturerRow(dr, newRow);
                }
                else
                {
                    DeleteManufacturerRow(dr);
                }
            }
            foreach (DataRow dr in hqTable.Rows)
            {
                string PrimaryKey = FindKey(ManufacturerPrimaryKey, dr);
                bool isFound = ShopManufacturerDic.ContainsKey(PrimaryKey);
                if (!isFound)
                {
                    AddManufacturerRow(dr);
                }
            }
        }

        private void AddManufacturerRow(DataRow newRow)
        {
            string name = newRow[Constant.MANUFACTURER_NAME].ToString();
            string address = newRow[Constant.MANUFACTURER_ADDRESS].ToString();
            string country = newRow[Constant.MANUFACTURER_COUNTRY].ToString();
            string contact = newRow[Constant.MANUFACTURER_CONTACT].ToString();
            DataTable table = DBController.GetShopDsTableList().Tables[Constant.MANUFACTURER_TABLE];
            table.Rows.Add(newRow[Constant.MANUFACTURER_NAME], newRow[Constant.MANUFACTURER_ADDRESS], 
                                newRow[Constant.MANUFACTURER_COUNTRY], newRow[Constant.MANUFACTURER_CONTACT]);
            string log = Constant.LOG_ADD + Constant.SPACE +
                    Constant.MANUFACTURER_NAME + Constant.PUNCTUATION_COLON + name + Constant.SPACE +
                    Constant.MANUFACTURER_ADDRESS + Constant.PUNCTUATION_COLON + address + Constant.SPACE +
                    Constant.MANUFACTURER_COUNTRY + Constant.PUNCTUATION_COLON + country + Constant.SPACE +
                    Constant.SHOP_CONTACT + Constant.PUNCTUATION_COLON + contact;
            SynLog.Log(log);
        }

        private void UpdateManufacturerRow(DataRow dr, DataRow newRow)
        {
            bool hasChanged = false;
            string name = newRow[Constant.MANUFACTURER_NAME].ToString();
            string address = newRow[Constant.MANUFACTURER_ADDRESS].ToString();
            string country = newRow[Constant.MANUFACTURER_COUNTRY].ToString();
            string contact = newRow[Constant.MANUFACTURER_CONTACT].ToString();
            if (!dr[Constant.MANUFACTURER_ADDRESS].ToString().Equals(address))
            {
                dr[Constant.MANUFACTURER_ADDRESS] = newRow[Constant.MANUFACTURER_ADDRESS];
                hasChanged = true;
            }
            if (!dr[Constant.MANUFACTURER_COUNTRY].ToString().Equals(country))
            {
                dr[Constant.MANUFACTURER_COUNTRY] = newRow[Constant.MANUFACTURER_COUNTRY];
                hasChanged = true;
            }
            if (!dr[Constant.MANUFACTURER_CONTACT].ToString().Equals(contact))
            {
                dr[Constant.MANUFACTURER_CONTACT] = newRow[Constant.MANUFACTURER_CONTACT];
                hasChanged = true;
            }

            if (hasChanged)
            {
                string log = Constant.LOG_UPDATE + Constant.SPACE +
                    Constant.MANUFACTURER_NAME + Constant.PUNCTUATION_COLON + name + Constant.SPACE +
                    Constant.MANUFACTURER_ADDRESS + Constant.PUNCTUATION_COLON + address + Constant.SPACE +
                    Constant.MANUFACTURER_COUNTRY + Constant.PUNCTUATION_COLON + country + Constant.SPACE +
                    Constant.SHOP_CONTACT + Constant.PUNCTUATION_COLON + contact;
                SynLog.Log(log);
            }
        }

        private void DeleteManufacturerRow(DataRow dr)
        {
            string name = dr[Constant.MANUFACTURER_NAME].ToString();
            string log = Constant.LOG_DELETE + Constant.SPACE + Constant.MANUFACTURER_NAME + Constant.PUNCTUATION_COLON + name;
            SynLog.Log(log);
            dr.Delete();
        }

        #endregion

        #region Product Operation

        private void SynchronizeProduct()
        {
            SynLog.Log(Constant.LOG_START_PRODUCT);
            DataTable shopTable = DBController.GetShopDsTableList().Tables[Constant.PRODUCT_TABLE];
            DataTable hqTable = DBController.GetHQDsTableList().Tables[Constant.PRODUCT_TABLE];
            try
            {
                Dictionary<string, DataRow> HQProductDic = StoreTableIntoDictionary(Constant.PRODUCT_TABLE, ProductPrimaryKey, Constant.HQ_SERVER);
                Dictionary<string, DataRow> ShopProductDic = StoreTableIntoDictionary(Constant.PRODUCT_TABLE, ProductPrimaryKey, Constant.SHOP_SERVER);
                SynchronizeProductRow(shopTable, hqTable, HQProductDic, ShopProductDic);
                DBController.UpdateDatabase(Constant.PRODUCT_TABLE);
            }
            catch
            {
                throw;
            }
            SynLog.Log(Constant.LOG_END_PRODUCT);
        }

        private void SynchronizeProductRow(DataTable shopTable, DataTable hqTable, Dictionary<string, DataRow> HQProductDic, Dictionary<string, DataRow> ShopProductDic)
        {
            foreach (DataRow dr in shopTable.Rows)
            {
                DataRow newRow;
                string PrimaryKey = FindKey(ProductPrimaryKey, dr);
                bool isFound = HQProductDic.TryGetValue(PrimaryKey, out newRow);
                if (isFound)
                {
                    UpdateProductRow(dr, newRow);
                }
                else
                {
                    DeleteProductRow(dr);
                }
            }
            int count = 0;
            foreach (DataRow dr in hqTable.Rows)
            {
                string PrimaryKey = FindKey(ProductPrimaryKey, dr);
                bool isFound = ShopProductDic.ContainsKey(PrimaryKey);
                
                if (!isFound)
                {
                    AddProductRow(dr);
                    count++;
                }
            }
        }

        //private void SynchronizeProductRow(Dictionary<string, DataRow> ProductDic, DataRow dr)
        //{
        //    DataRow newRow;
        //    string PrimaryKey = FindKey(ProductPrimaryKey, dr);
        //    bool isFound = ProductDic.TryGetValue(PrimaryKey, out newRow);
        //    if (isFound)
        //    {
        //        UpdateProductRow(dr, newRow);
        //    }
        //    else
        //    {
        //        DeleteProductRow(dr);
        //    }
        //}

        private void AddProductRow(DataRow newRow)
        {
            string manufacturerName = newRow[Constant.PRODUCT_MANUFACTURER_NAME].ToString();
            string productID = newRow[Constant.PRODUCT_ID].ToString();
            string name = newRow[Constant.PRODUCT_NAME].ToString();
            string category = newRow[Constant.PRODUCT_CATEGORY].ToString();
            string minStock = newRow[Constant.PRODUCT_MINIMUM_STOCK].ToString();
            DataTable table = DBController.GetShopDsTableList().Tables[Constant.PRODUCT_TABLE];
            table.Rows.Add(newRow[Constant.PRODUCT_MANUFACTURER_NAME], newRow[Constant.PRODUCT_ID], newRow[Constant.PRODUCT_NAME],
                        newRow[Constant.PRODUCT_CATEGORY], newRow[Constant.PRODUCT_MINIMUM_STOCK]);
            string log = Constant.LOG_ADD + Constant.SPACE +
                    Constant.PRODUCT_MANUFACTURER_NAME + Constant.PUNCTUATION_COLON + manufacturerName + Constant.SPACE +
                    Constant.PRODUCT_ID + Constant.PUNCTUATION_COLON + productID + Constant.SPACE +
                    Constant.PRODUCT_NAME + Constant.PUNCTUATION_COLON + name + Constant.SPACE +
                    Constant.PRODUCT_CATEGORY + Constant.PUNCTUATION_COLON + category + Constant.SPACE +
                    Constant.PRODUCT_MINIMUM_STOCK + Constant.PUNCTUATION_COLON + minStock;
            SynLog.Log(log);
        }

        private void UpdateProductRow(DataRow dr, DataRow newRow)
        {
            bool hasChanged = false;
            string manufacturerName = newRow[Constant.PRODUCT_MANUFACTURER_NAME].ToString();
            string id = newRow[Constant.PRODUCT_ID].ToString();
            string name = newRow[Constant.PRODUCT_NAME].ToString();
            string category = newRow[Constant.PRODUCT_CATEGORY].ToString();
            if (!dr[Constant.PRODUCT_MANUFACTURER_NAME].ToString().Equals(manufacturerName))
            {
                dr[Constant.PRODUCT_MANUFACTURER_NAME] = newRow[Constant.PRODUCT_MANUFACTURER_NAME];
                hasChanged = true;
            }
            if (!dr[Constant.PRODUCT_NAME].ToString().Equals(name))
            {
                dr[Constant.PRODUCT_NAME] = newRow[Constant.PRODUCT_NAME];
                hasChanged = true;
            }
            if (!dr[Constant.PRODUCT_CATEGORY].ToString().Equals(category))
            {
                dr[Constant.PRODUCT_CATEGORY] = newRow[Constant.PRODUCT_CATEGORY];
                hasChanged = true;
            }

            if (hasChanged)
            {
                string log = Constant.LOG_UPDATE + Constant.SPACE +
                    Constant.PRODUCT_MANUFACTURER_NAME + Constant.PUNCTUATION_COLON + manufacturerName + Constant.SPACE +
                    Constant.PRODUCT_ID + Constant.PUNCTUATION_COLON + id + Constant.SPACE +
                    Constant.PRODUCT_NAME + Constant.PUNCTUATION_COLON + name + Constant.SPACE +
                    Constant.PRODUCT_CATEGORY + Constant.PUNCTUATION_COLON + category;
                SynLog.Log(log);
            }
        }

        private void DeleteProductRow(DataRow dr)
        {
            string id = dr[Constant.PRODUCT_ID].ToString();
            string log = Constant.LOG_DELETE + Constant.SPACE + Constant.PRODUCT_ID + Constant.PUNCTUATION_COLON + id;
            SynLog.Log(log);
            dr.Delete();
        }

        #endregion

        #region Stock Operation

        public void SynchronizeStock()
        {
            SynLog.Log(Constant.LOG_START_STOCK);
            DataTable table = DBController.GetShopDsTableList().Tables[Constant.STOCK_TABLE];
            try
            {
                Dictionary<string, DataRow> StockDic = StoreTableIntoDictionary(Constant.STOCK_TABLE, StockPrimaryKey, Constant.HQ_SERVER);
                foreach (DataRow dr in table.Rows)
                {
                    SynchronizeStockRow(StockDic, dr);
                }
                DBController.UpdateDatabase(Constant.STOCK_TABLE);
            }
            catch
            {
                throw;
            }
            SynLog.Log(Constant.LOG_END_STOCK);
        }

        private void SynchronizeStockRow(Dictionary<string, DataRow> StockDic, DataRow dr)
        {
            DataRow newRow;
            string PrimaryKey = FindKey(StockPrimaryKey, dr);
            bool isFound = StockDic.TryGetValue(PrimaryKey, out newRow);
            if (isFound)
            {
                UpdateStockRow(dr, newRow);
            }
            else
            {
                DeleteStockRow(dr);
            }
        }

        private void UpdateStockRow(DataRow dr, DataRow newRow)
        {
            bool hasChange = false;
            string productId = newRow[Constant.STOCK_PRODUCT_ID].ToString();
            string batchId = newRow[Constant.STOCK_BATCH_ID].ToString();
            string importPrice = newRow[Constant.STOCK_IMPORT_PRICE].ToString();
            string sellingPrice = newRow[Constant.STOCK_SELLING_PRICE].ToString();
            string expireDate = newRow[Constant.STOCK_EXPIRE_DATE].ToString();
            if (!dr[Constant.STOCK_IMPORT_PRICE].ToString().Equals(importPrice))
            {
                dr[Constant.STOCK_IMPORT_PRICE] = newRow[Constant.STOCK_IMPORT_PRICE];
                hasChange = true;
            }
            if (!dr[Constant.STOCK_SELLING_PRICE].ToString().Equals(sellingPrice))
            {
                dr[Constant.STOCK_SELLING_PRICE] = newRow[Constant.STOCK_SELLING_PRICE];
                hasChange = true;
            }
            if (!dr[Constant.STOCK_EXPIRE_DATE].ToString().Equals(expireDate))
            {
                dr[Constant.STOCK_EXPIRE_DATE] = newRow[Constant.STOCK_EXPIRE_DATE];
                hasChange = true;
            }

            if (hasChange)
            {
                string log = Constant.LOG_UPDATE + Constant.SPACE +
                    Constant.STOCK_PRODUCT_ID + Constant.PUNCTUATION_COLON + productId + Constant.SPACE +
                    Constant.STOCK_BATCH_ID + Constant.PUNCTUATION_COLON + batchId + Constant.SPACE +
                    Constant.STOCK_IMPORT_PRICE + Constant.PUNCTUATION_COLON + importPrice + Constant.SPACE +
                    Constant.STOCK_SELLING_PRICE + Constant.PUNCTUATION_COLON + sellingPrice + Constant.SPACE +
                    Constant.STOCK_EXPIRE_DATE + Constant.PUNCTUATION_COLON + expireDate;
                SynLog.Log(log);
            }
        }

        private void DeleteStockRow(DataRow dr)
        {
            string productId = dr[Constant.STOCK_PRODUCT_ID].ToString();
            string batchId = dr[Constant.STOCK_BATCH_ID].ToString();
            string log = Constant.LOG_DELETE + Constant.SPACE +
                Constant.STOCK_PRODUCT_ID + Constant.PUNCTUATION_COLON + productId + Constant.SPACE +
                Constant.STOCK_BATCH_ID + Constant.PUNCTUATION_COLON + batchId;
            SynLog.Log(log);
            dr.Delete();
        }

        #endregion

        #region Discount Operation

        public void SynchronizeDiscount()
        {
            SynLog.Log(Constant.LOG_START_DISCOUNT);
            DataTable table = DBController.GetShopDsTableList().Tables[Constant.PRODUCT_TABLE];
            try
            {
                Dictionary<string, DataRow> ShopDiscountDict = StoreTableIntoDictionary(Constant.DISCOUNT_TABLE, DiscountPrimaryKey, Constant.SHOP_SERVER);
                Dictionary<string, DataRow> HQDiscountDict = StoreTableIntoDictionary(Constant.DISCOUNT_TABLE, DiscountPrimaryKey, Constant.HQ_SERVER);
                foreach (DataRow dr in table.Rows)
                {
                    if(dr != null)
                        SynchronizeDiscountRow(ShopDiscountDict, HQDiscountDict, dr);
                }
                DBController.UpdateDatabase(Constant.DISCOUNT_TABLE);
            }
            catch
            {
                throw;
            }
            SynLog.Log(Constant.LOG_END_DISCOUNT);
        }

        private void SynchronizeDiscountRow(Dictionary<string, DataRow> ShopDiscountDict, Dictionary<string, DataRow> HQDiscountDict, DataRow dr)
        {
            DataRow HQDiscountRow;
            DataRow ShopDiscountRow;
            string primaryKey = FindKey(ProductPrimaryKey, dr);
            bool isHQDiscountFound = HQDiscountDict.TryGetValue(primaryKey, out HQDiscountRow);
            bool isShopDiscountFound = ShopDiscountDict.TryGetValue(primaryKey, out ShopDiscountRow);
            if (isHQDiscountFound && isShopDiscountFound)
            {
                UpdateDiscountRow(ShopDiscountRow, HQDiscountRow);
            }
            else if (!isHQDiscountFound && isShopDiscountFound)
            {
                DeleteDiscountRow(ShopDiscountRow);
            }
            else if (isHQDiscountFound && !isShopDiscountFound)
            {
                AddDiscountRow(HQDiscountRow);
            }
        }

        private void UpdateDiscountRow(DataRow dr, DataRow newRow)
        {
            bool hasChanged = false;
            string productId = newRow[Constant.DISCOUNT_PRODUCT_ID].ToString();
            string effectiveDate = newRow[Constant.DISCOUNT_EFFECTIVE_DATE].ToString();
            string bundleUnit = newRow[Constant.DISCOUNT_BUNDLEUNIT].ToString();
            string freeItemQuantity = newRow[Constant.DISCOUNT_FREE_ITEM_QUANTITY].ToString();
            string discount = newRow[Constant.DISCOUNT_DISCOUNT].ToString();
            if (!dr[Constant.DISCOUNT_EFFECTIVE_DATE].ToString().Equals(effectiveDate))
            {
                dr[Constant.DISCOUNT_EFFECTIVE_DATE] = newRow[Constant.DISCOUNT_EFFECTIVE_DATE];
                hasChanged = true;
            }
            if (!dr[Constant.DISCOUNT_BUNDLEUNIT].ToString().Equals(bundleUnit))
            {
                dr[Constant.DISCOUNT_BUNDLEUNIT] = newRow[Constant.DISCOUNT_BUNDLEUNIT];
                hasChanged = true;
            }
            if (!dr[Constant.DISCOUNT_FREE_ITEM_QUANTITY].ToString().Equals(freeItemQuantity))
            {
                dr[Constant.DISCOUNT_FREE_ITEM_QUANTITY] = newRow[Constant.DISCOUNT_FREE_ITEM_QUANTITY];
                hasChanged = true;
            }
            if (!dr[Constant.DISCOUNT_DISCOUNT].ToString().Equals(discount))
            {
                dr[Constant.DISCOUNT_DISCOUNT] = newRow[Constant.DISCOUNT_DISCOUNT];
                hasChanged = true;
            }

            if (hasChanged)
            {
                string log = Constant.LOG_UPDATE + Constant.SPACE +
                    Constant.DISCOUNT_PRODUCT_ID + Constant.PUNCTUATION_COLON + productId + Constant.SPACE +
                    Constant.DISCOUNT_EFFECTIVE_DATE + Constant.PUNCTUATION_COLON + effectiveDate + Constant.SPACE +
                    Constant.DISCOUNT_BUNDLEUNIT + Constant.PUNCTUATION_COLON + bundleUnit + Constant.SPACE +
                    Constant.DISCOUNT_FREE_ITEM_QUANTITY + Constant.PUNCTUATION_COLON + freeItemQuantity + Constant.SPACE +
                    Constant.DISCOUNT_DISCOUNT + Constant.PUNCTUATION_COLON + discount;
                SynLog.Log(log);
            }
        }

        private void DeleteDiscountRow(DataRow dr)
        {
            string productId = dr[Constant.DISCOUNT_PRODUCT_ID].ToString();
            string log = Constant.LOG_DELETE + Constant.SPACE +
                Constant.DISCOUNT_PRODUCT_ID + Constant.PUNCTUATION_COLON + productId + Constant.SPACE;
            SynLog.Log(log);
            dr.Delete();
        }

        private void AddDiscountRow(DataRow newRow)
        {
            string productId = newRow[Constant.DISCOUNT_PRODUCT_ID].ToString();
            DateTime effectiveDate = Convert.ToDateTime(newRow[Constant.DISCOUNT_EFFECTIVE_DATE]);
            string bundleUnit = newRow[Constant.DISCOUNT_BUNDLEUNIT].ToString();
            string freeItemQuantity = newRow[Constant.DISCOUNT_FREE_ITEM_QUANTITY].ToString();
            string discount = newRow[Constant.DISCOUNT_DISCOUNT].ToString();

            string log = Constant.LOG_ADD + Constant.SPACE +
                Constant.DISCOUNT_PRODUCT_ID + Constant.PUNCTUATION_COLON + productId + Constant.SPACE +
                Constant.DISCOUNT_EFFECTIVE_DATE + Constant.PUNCTUATION_COLON + effectiveDate.ToString() + Constant.SPACE +
                Constant.DISCOUNT_BUNDLEUNIT + Constant.PUNCTUATION_COLON + bundleUnit + Constant.SPACE +
                Constant.DISCOUNT_FREE_ITEM_QUANTITY + Constant.PUNCTUATION_COLON + freeItemQuantity + Constant.SPACE +
                Constant.DISCOUNT_DISCOUNT + Constant.PUNCTUATION_COLON + discount;
            SynLog.Log(log);

            DataTable table = DBController.GetShopDsTableList().Tables[Constant.DISCOUNT_TABLE];
            table.Rows.Add(productId, effectiveDate, bundleUnit, freeItemQuantity, discount);
        }

        #endregion

        #region Send Report Operation

        public void SynchronizeReport()
        {
            SynLog.Log(Constant.LOG_START_REPORT);
            DataTable table = DBController.GetShopDsTableList().Tables[Constant.REPORT_TABLE];
            try
            {
                Dictionary<string, DataRow> ReportDict = StoreTableIntoDictionary(Constant.REPORT_TABLE, ReportPrimaryKey, Constant.HQ_SERVER);

                foreach (DataRow dr in table.Rows)
                {
                    SynchronizeReportRow(ReportDict, dr);
                }
                DBController.UpdateDatabase(Constant.REPORT_TABLE);
            }
            catch
            {
                throw;
            }
            SynLog.Log(Constant.LOG_END_REPORT);
        }

        private void SynchronizeReportRow(Dictionary<string, DataRow> ReportDict, DataRow dr)
        {
            DataRow newRow;
            string PrimaryKey = FindKey(ReportPrimaryKey, dr);
            bool isFound = ReportDict.TryGetValue(PrimaryKey, out newRow);
            if (!isFound)
            {
                AddReportRow(dr);
            }
        }

        private void AddReportRow(DataRow dr)
        {
            string shopId = dr[Constant.REPORT_SHOP_ID].ToString();
            DateTime reportDate = Convert.ToDateTime(dr[Constant.REPORT_DATE]);
            string totalCostPrice = dr[Constant.REPORT_TOTAL_COST_PRICE].ToString();
            string totalSellingPrice = dr[Constant.REPORT_TOTAL_SELLING_PRICE].ToString();
            string sellingQuantity = dr[Constant.REPORT_SELLING_QUANTITY].ToString();

            string log = Constant.LOG_ADD + Constant.SPACE +
                Constant.REPORT_SHOP_ID + Constant.PUNCTUATION_COLON + shopId + Constant.SPACE +
                Constant.REPORT_DATE + Constant.PUNCTUATION_COLON + reportDate.ToString() + Constant.SPACE +
                Constant.REPORT_TOTAL_COST_PRICE + Constant.PUNCTUATION_COLON + totalCostPrice + Constant.SPACE +
                Constant.REPORT_TOTAL_SELLING_PRICE + Constant.PUNCTUATION_COLON + totalSellingPrice + Constant.SPACE +
                Constant.REPORT_SELLING_QUANTITY + Constant.PUNCTUATION_COLON + sellingQuantity;
            SynLog.Log(log);

            DataTable table = DBController.GetHQDsTableList().Tables[Constant.REPORT_TABLE];
            table.Rows.Add(shopId, reportDate, totalCostPrice, totalSellingPrice, sellingQuantity);
        }

        #endregion

        #region Update Stock Price

        public void UpdateStockPrice()
        {
            try
            {
                DateTime synTime = DateTime.Now;
                DBController.RefleshShopStock();
                SynLog.Log(synTime.ToString());
                SynLog.Log(Constant.LOG_START_DAILY_UPDATE);
                updateSellingPrice();
                SynLog.Log(Constant.LOG_END_DAILY_UPDATE);
            }
            catch
            {
                throw;
            }
        }

        private void updateSellingPrice()
        {
            DataTable stock = DBController.GetShopDsTableList().Tables[Constant.STOCK_TABLE];
            foreach (DataRow dr in stock.Rows)
            {
                if (IsAlmostExpired(dr))
                {
                    UpdateStockExpiredDateDiscount(dr);
                }
                else
                {
                    dr[Constant.STOCK_DEFAULT_DISCOUNT_POLICY] = false;
                }
            }
            DBController.UpdateDatabase(Constant.STOCK_TABLE);
            DataTable test = stock.GetChanges();
        }

        private void UpdateStockExpiredDateDiscount(DataRow dr)
        {
            string productId = dr[Constant.STOCK_PRODUCT_ID].ToString();
            double sellPrice = Convert.ToDouble(dr[Constant.STOCK_SELLING_PRICE]) * Constant.STOCK_EXPIREDATE_DISCOUNT;
            dr[Constant.STOCK_SELLING_PRICE] = sellPrice.ToString();
            dr[Constant.STOCK_DEFAULT_DISCOUNT_POLICY] = true;
            string log = Constant.LOG_UPDATE + Constant.SPACE +
                    Constant.STOCK_PRODUCT_ID + Constant.PUNCTUATION_COLON + productId + Constant.SPACE +
                    sellPrice + Constant.LOG_FROM + dr[Constant.STOCK_SELLING_PRICE].ToString() +
                    Constant.LOG_TO + sellPrice.ToString();
            SynLog.Log(log);
        }

        private bool IsAlmostExpired(DataRow dr)
        {
            DateTime batchId = Convert.ToDateTime(dr[Constant.STOCK_BATCH_ID]);
            DateTime expireDate = Convert.ToDateTime(dr[Constant.STOCK_EXPIRE_DATE]);
            DateTime now = DateTime.Now;
            TimeSpan ShelfLife = expireDate - batchId;
            double ShelfLifeSec = ShelfLife.TotalSeconds;
            TimeSpan NowToExpired = expireDate - now;
            double NowToExpiredSec = NowToExpired.TotalSeconds;
            if (ShelfLifeSec * Constant.SHELF_LIFE_PERCENTAGE > NowToExpiredSec)
            {
                return true;
            }
            return false;
        }

        #endregion
    }
}
